#include "glass_shader.hh"

using namespace std;
using namespace cgmath;

glass_shader::glass_shader (double refraction_index_t)
  : refraction_index (refraction_index_t)
{}

// in_dir points from source (e.g. light sample) to this point.
vector_3d glass_shader::bsdf (const surface_point& point,
				const vector_3d&     in_dir,
				const vector_3d&  /* out_dir */)
{
  return vec (0.0, 0.0, 0.0);
}

/// Generate one sample that is reflection of the out going direction.
void glass_shader::sample_bsdf (const surface_point& point,
                                 unsigned             num_samples,
                                 const vector_3d&     out_dir,
                                 bsdf_samples& samples)
{
  samples.resize (1);

  // Snell's law: ni * sin(theta_i) = nt * sin(theta_t)
  // 
  // There are two cases to consider:
  // Case 1: out_dir points to the outside of the surface.
  //         Then the ray is coming into the material and
  //         ni = 1,
  //         nt = refraction_index.
  //
  // Case 2: out_dir points to the inside of the surface.
  //         Then the ray is going out of the material and
  //         ni = refraction_index
  //         nt = 1

  double ni;
  double nt;
  vector_3d normal;
  if (abs (dot (point.normal, out_dir)) < 1e-6)
    {
      samples[0] = bsdf_sample (-out_dir, vec (1.0, 1.0, 1.0));
      return;
    }
  else if (dot (point.normal, out_dir) > 0) // Case 1
    {
      ni = 1.0;
      nt = refraction_index;
      normal = point.normal;
    }
  else // Case 2
    {
      ni = refraction_index;
      nt = 1.0;
      normal = -point.normal;
    }

  double sin_i = norm (out_dir ^ normal) /
    (norm (out_dir) * norm (normal));
  double sin_t = ni / nt * sin_i;
  double theta_i = asin (sin_i);
  double theta_t = asin (sin_t);

  double theta = theta_i + (M_PI - theta_t);
  vector_3d rotation_axis = out_dir ^ normal;

  matrix_3d R = rotate (theta, rotation_axis);
  vector_3d in_dir = R * out_dir;

  samples[0] = bsdf_sample (in_dir, vec (1.0, 1.0, 1.0));
}
